#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation; either version 2 of the License, or     
# (at your option) any later version.                                   
#                                                                         
# A copy of the license can be found in the license.txt file supplied   
# with this software or at: http://www.gnu.org/copyleft/gpl.html       
#

import struct

import tc

from icewood import tinylog


class   TyrantIndex(object) :
    
    HDB_INUM = 4
    HDB_BNUM = 10485760
    HDB_APOW = 4
    HDB_FPOW = 10
    HDB_XMSIZ = 268435456

    BDB_INUM = 16
    BDB_LMEMB = 512
    BDB_NMEMB = 1024
    BDB_BNUM = 10485760
    BDB_APOW = 8
    BDB_FPOW = 10
    BDB_XMSIZ = 0
    BDB_LCNUM = 12288
    BDB_NCNUM = 512
    BDB_DFUNIT = 8

    def __init__(self, filename, options=None) :
        self._logger = tinylog.getLogger( self.__class__.__name__ )

        self._db_filename = filename

        self._logger.info('initializing Tyrant index %s ...',self._db_filename)
        
        if options :
            if 'hdb' == options['engine'] :
                self.__initHDB( options )
            elif 'bdb' == options['engine'] :
                self.__initBDB( options )
            else :
                self.__initBDB( options )
        else :        
            self.__initBDB( options )

        self._logger.info('initialize Tyrant index %s done.',self._db_filename)
        ## __init__()

    def __initBDB(self, options) :
        if options :
            try :
                inum = options['inum']
            except :
                inum = self.BDB_INUM

            try :
                lmemb = options['lmemb']
            except :
                lmemb = self.BDB_LMEMB

            try :
                nmemb = options['nmemb']
            except :
                nmemb = self.BDB_NMEMB

            try :
                bnum = options['bnum']
            except :
                bnum = self.BDB_BNUM

            try :
                apow = options['apow']
            except :
                apow = self.BDB_APOW

            try :
                fpow = options['fpow']
            except :
                fpow = self.BDB_FPOW

            try :
                xmsiz = options['xmsiz']
            except :
                xmsiz = self.BDB_XMSIZ

            try :
                lcnum = options['lcnum']
            except :
                lcnum = self.BDB_LCNUM

            try :
                ncnum = options['ncnum']
            except :
                ncnum = self.BDB_NCNUM

            try :
                dfunit = options['dfunit']
            except :
                dfunit = self.BDB_DFUNIT
        else :
            inum, lmemb,nmemb,bnum,apow,fpow,xmsiz = self.BDB_INUM, self.BDB_LMEMB,self.BDB_NMEMB,self.BDB_BNUM, self.BDB_APOW, self.BDB_FPOW, self.BDB_XMSIZ
            lcnum,ncnum = self.BDB_LCNUM, self.BDB_NCNUM
            dfunit = self.BDB_DFUNIT

        self._dbs = []
        for i in xrange(inum) :
            db = tc.BDB()
            #db.tune(bnum, apow, fpow, tc.HDBTTCBS)
            db.tune(lmemb,nmemb, bnum, apow, fpow, 1)
            if xmsiz > 0 :
                db.setxmsiz( xmsiz )
            if (lcnum > 0) and (ncnum > 0) :
                db.setcache(lcnum, ncnum)
            if dfunit > 0 :
                db.setdfunit( dfunit )

            filename = '%s-i%02X' % (self._db_filename, i)
            db.open(filename, tc.HDBOWRITER | tc.HDBOCREAT)

            self._dbs.append( db )
        self._dbs_count = inum
        ## __initBDB()

    def __initHDB(self, options) :
        if options :
            try :
                inum = options['inum']
            except :
                inum = self.HDB_INUM

            try :
                bnum = options['bnum']
            except :
                bnum = self.HDB_BNUM

            try :
                apow = options['apow']
            except :
                apow = self.HDB_APOW

            try :
                fpow = options['fpow']
            except :
                fpow = self.HDB_FPOW

            try :
                xmsiz = options['xmsiz']
            except :
                xmsiz = self.HDB_XMSIZ
        else :
            inum, bnum,apow,fpow,xmsiz = self.HDB_INUM, self.HDB_BNUM, self.HDB_APOW, self.HDB_FPOW, self.HDB_XMSIZ

        self._dbs = []
        for i in xrange(inum) :
            db = tc.HDB()
            #db.tune(bnum, apow, fpow, tc.HDBTTCBS)
            db.tune(bnum, apow, fpow, 1)
            if xmsiz > 0 :
                db.setxmsiz( xmsiz )
            filename = '%s-i%02X' % (self._db_filename, i)
            db.open(filename, tc.HDBOWRITER | tc.HDBOCREAT)

            self._dbs.append( db )
        self._dbs_count = inum
        ## __initHDB()

    def __del__(self) :
        self.close()
        ## __del__()

    def close(self) :
        if self._dbs :
            for db in self._dbs :
                db.sync()
                db.close()
                db = None

            self._dbs = []
        ## close()

    def flush(self) :
        if self._dbs :
            for db in self._dbs :
                db.sync()
        ## flush()

    def put(self, key, value) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        db.put( key_s, value )
        ## put()

    def get(self, key) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        try :
            return db[ key_s ]
        except KeyError :
            return None
        ## get()

    def delete(self, key) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        try :
            del db[ key_s ]
        except KeyError :
            return True

        return True
        ## delete()


    def vanish(self) :
        for db in self._dbs :
            db.vanish()
        ## vanish()

    ###################################################################

    def __getHashCode(self, key) :
        k_idx = struct.unpack('>H', key[4:6])[0]

        return k_idx % self._dbs_count
        ## __getDB()

    def __getDB(self, key) :
        idx = self.__getHashCode( key )

        return self._dbs[ idx ]
        ## __getDB()
 
    def __getStoreKey(self, key) :
        return  key[:8]
        ## __getStoreKey()

    ## class TyrantIndex


